package cn.ichiva.service;

import cn.ichiva.vo.ApiVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
@Service
public class RedisService {
    private final ValueService value;
    private final HashService hash;
    private final ListService list;
    private final SetService set;
    private final ZSetService zset;
    private final KeyService key;

    private final Map<String, Method> valueMethodMap;
    private final Map<String, Method> hashMethodMap;
    private final Map<String, Method> listMethodMap;
    private final Map<String, Method> setMethodMap;
    private final Map<String, Method> zsetMethodMap;
    private final Map<String, Method> keyMethodMap;

    public RedisService(RedisTemplate<String, String> redisTemplate) {
        value = new ValueService(redisTemplate.opsForValue());
        hash = new HashService(redisTemplate.opsForHash());
        list = new ListService(redisTemplate.opsForList());
        set = new SetService(redisTemplate.opsForSet());
        zset = new ZSetService(redisTemplate.opsForZSet());
        this.key= new KeyService(redisTemplate);

        Set<String> excludeSet = Arrays.stream(Object.class.getMethods())
                .map(Method::getName)
                .collect(Collectors.toSet());

        valueMethodMap = getMethodMap(value,excludeSet);
        hashMethodMap = getMethodMap(hash,excludeSet);
        listMethodMap = getMethodMap(list,excludeSet);
        setMethodMap = getMethodMap(list,excludeSet);
        zsetMethodMap = getMethodMap(list,excludeSet);
        keyMethodMap = getMethodMap(key,excludeSet);
    }

    private Map<String, Method> getMethodMap(Object value,Set<String> set) {
        Map<String,Method> res = new HashMap<>();
        //排除Object的方
        Class<?> aClass = value.getClass();
        for (Method method : aClass.getMethods()) {
            String name = method.getName();
            if(!set.contains(name)){
                Method put = res.put(name, method);
                if(put != null){
                    throw new RuntimeException(String.format("方法重名:%s.%s",aClass.getName(),method.getName()));
                }
            }
        }

        res.forEach((k,v) -> log.info("{} -> {}",aClass.getSimpleName(),k));
        return res;
    }


    public Object exec(ApiVO vo){
        String type = vo.getType();
        Method method;
        Object obj;

        if("value".equals(type)){
            method = valueMethodMap.get(vo.getAct());
            obj = value;
        }else if("hash".equals(type)){
            method = hashMethodMap.get(vo.getAct());
            obj = hash;
        }else if("list".equals(type)){
            method = listMethodMap.get(vo.getAct());
            obj = list;
        }else if("set".equals(type)){
            method = setMethodMap.get(vo.getAct());
            obj = set;
        }else if("zset".equals(type)){
            method = zsetMethodMap.get(vo.getAct());
            obj = zset;
        }else if("key".equals(type)){
            method = keyMethodMap.get(vo.getAct());
            obj = key;
        } else {
            throw new UnsupportedOperationException("不支持的数据类型");
        }

        if(null == method){
            throw new RuntimeException("未知方法");
        }

        try {
            return method.invoke(obj,vo.getArgs());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}
